﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Net;
using System.Web.Services.Protocols;
using System.Runtime.InteropServices;
using StockholmsStad.Utilities.ReportingService;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Utilities.Properties;
using System.Threading;

namespace StockholmsStad.Utilities
{
    public class ReportUtility
    {
        private static NetworkCredential credentials;
        private readonly Hashtable printProperties;

        private int settingsCounter = 0;

        struct PrintPropertiesStruct 
        {
            public int noOfPages;
            public int curPrintingPage;
            public int lstPrintingPage;
            public MemoryStream currentPageStream;
            public Metafile metafile;           
            public ReportExecutionService rsExecutionService;
            public string reportname;
        };


        public ReportUtility()
        {

            printProperties = new Hashtable();

            // Create proxy object and authenticate
            Console.WriteLine("Authenticating to the Web service...");

            var username = SettingsUtility.GetAppSetting(Resources.ReportUser);
            var password = SettingsUtility.GetAppSetting(Resources.ReportPassword);

            credentials = new NetworkCredential(username, password);
        }

        public void PrintMarriageCertificate(DateTime date, int coupleId, int languageId)
        {

            // Set report parameters
            var parameterList = new List<ReportParam>
                                        {
                                            new ReportParam
                                                {
                                                    ParameterName = "date",
                                                    ParameterValue = date.ToString()
                                                },
                                            new ReportParam
                                                {
                                                    ParameterName = "coupleID",
                                                    ParameterValue = coupleId.ToString()
                                                },
                                            new ReportParam
                                                {
                                                    ParameterName = "LangID",
                                                    ParameterValue = languageId.ToString()
                                                }
                                        };

            PrintMarriageCertificate(parameterList);
        }

        private void PrintMarriageCertificate(object reportParams)
        {
            try
            {
                var rsExecService = new ReportExecutionService
                {
                    Credentials = credentials,
                    Url = SettingsUtility.GetAppSetting(Resources.ReportServer)
                };

                // Set report path
                var reportPath = SettingsUtility.GetAppSetting(Resources.MarriageCertificateReportPath);
                rsExecService.LoadReport(reportPath, null);

                var parameters = SetReportParameters((List<ReportParam>)reportParams);
                rsExecService.SetExecutionParameters(parameters, "en-us");

                // Init properties struct
                var printPropertiesStruct = new PrintPropertiesStruct
                {
                    rsExecutionService = rsExecService,
                    reportname = Enum.GetName(typeof(ReportType),ReportType.MarriageCertificate)
                };

                settingsCounter = settingsCounter + 1;

                // Add struct to print properities hashtable
                printProperties.Add(settingsCounter, printPropertiesStruct);

                // Render report to PDF and print
                //RenderAndPrintPDF(ReportType.MarriageCertificate, Enum.GetName(typeof(ReportType), ReportType.MarriageCertificate));

                //PrintPDF();

                //Print report
                PrintReport(ReportType.MarriageCertificate, Enum.GetName(typeof(ReportType), ReportType.MarriageCertificate));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in thread: " + e.Message);
                throw;
            }
        }

        public void PrintMarriageRegister(DateTime date, int ministerId)
        {
            // Set report parameters
            var parameterList = new List<ReportParam>
                                    {
                                        new ReportParam
                                            {
                                                ParameterName = "date",
                                                ParameterValue = date.ToShortDateString()
                                            },
                                        new ReportParam
                                            {
                                                ParameterName = "ministerId",
                                                ParameterValue = ministerId.ToString()
                                            }
                                    };

            PrintMarriageRegister(parameterList);
        }

        private void PrintMarriageRegister(object reportParams)
        {
            try
            {
                var rsExecService = new ReportExecutionService
                {
                    Credentials = credentials,
                    Url = SettingsUtility.GetAppSetting(Resources.ReportServer)
                };

                // Set report path
                var reportPath = SettingsUtility.GetAppSetting(Resources.MarriageRegisterReportPath);
                rsExecService.LoadReport(reportPath, null);

                var parameters = SetReportParameters((List<ReportParam>)reportParams);
                rsExecService.SetExecutionParameters(parameters, "en-us");

                // Init properties struct
                var printPropertiesStruct = new PrintPropertiesStruct
                {
                    rsExecutionService = rsExecService,
                    reportname = Enum.GetName(typeof(ReportType), ReportType.MarriageRegister)
                };

                settingsCounter = settingsCounter + 1;

                // Add struct to print properities hashtable                
                printProperties.Add(settingsCounter, printPropertiesStruct);

                // Render report to PDF and print
                //RenderAndPrintPDF(ReportType.MarriageRegister, Enum.GetName(typeof(ReportType), ReportType.MarriageRegister));

                //Print report
                PrintReport(ReportType.MarriageRegister, Enum.GetName(typeof(ReportType), ReportType.MarriageRegister));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in thread: " + e.Message);
                throw;
            }
        }

        public void PrintMarriageReport(int coupleId)
        {

            // Set report parameters
            var parameterList = new List<ReportParam>
                                    {
                                        new ReportParam
                                            {
                                                ParameterName = "coupleId",
                                                ParameterValue = coupleId.ToString()
                                            }
                                    };

            PrintMarriageRegistrationForm(parameterList);
        }

        private void PrintMarriageRegistrationForm(object reportParams)
        {
            try
            {
                var rsExecService = new ReportExecutionService
                {
                    Credentials = credentials,
                    Url = SettingsUtility.GetAppSetting(Resources.ReportServer)
                };

                // Set report path
                var reportPath = SettingsUtility.GetAppSetting(Resources.MarriageRegistrationFormReportPath);
                rsExecService.LoadReport(reportPath, null);

                var parameters = SetReportParameters((List<ReportParam>)reportParams);
                rsExecService.SetExecutionParameters(parameters, "en-us");

                // Init properties struct
                var printPropertiesStruct = new PrintPropertiesStruct
                {
                    rsExecutionService = rsExecService,
                    reportname = Enum.GetName(typeof(ReportType), ReportType.MarriageRegistrationForm)
                };

                settingsCounter = settingsCounter + 1;

                // Add struct to print properities hashtable               
                printProperties.Add(settingsCounter, printPropertiesStruct);

                // Render report to PDF and print
                //RenderAndPrintPDF(ReportType.MarriageRegistrationForm, Enum.GetName(typeof(ReportType), ReportType.MarriageRegistrationForm));
                
                //Print report
                PrintReport(ReportType.MarriageRegistrationForm, Enum.GetName(typeof(ReportType), ReportType.MarriageRegistrationForm));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in thread: " + e.Message);
                throw;
            }
        }

        public void PrintReimbursement(int ministerId, DateTime fromDate)
        {
            // Set report parameters
            var parameterList = new List<ReportParam>
                                    {
                                        new ReportParam
                                            {ParameterName = "ministerId", ParameterValue = ministerId.ToString()},
                                        new ReportParam
                                            {ParameterName = "fromDate", ParameterValue = fromDate.ToString()}
                                    };            

            PrintReimbursement(parameterList);
        }

        private void PrintReimbursement(object reportParams)
        {
            try
            {               
                var rsExecService = new ReportExecutionService
                {
                    Credentials = credentials,
                    Url = SettingsUtility.GetAppSetting(Resources.ReportServer)
                };

                // Set report path
                var reportPath = SettingsUtility.GetAppSetting(Resources.ReimbursementReportPath);
                rsExecService.LoadReport(reportPath, null);

                var parameters = SetReportParameters((List<ReportParam>)reportParams);
                rsExecService.SetExecutionParameters(parameters, "en-us");

                // Init properties struct
                var printPropertiesStruct = new PrintPropertiesStruct
                {
                    rsExecutionService = rsExecService,
                    reportname = Enum.GetName(typeof(ReportType), ReportType.Reimbursement)
                };

                // Add struct to print properities hashtable
                printProperties.Add(settingsCounter, printPropertiesStruct);

                // Render report to PDF and print
                //RenderAndPrintPDF(ReportType.Reimbursement, Enum.GetName(typeof(ReportType), ReportType.Reimbursement));
                
                //Print report
                PrintReport(ReportType.Reimbursement, Enum.GetName(typeof(ReportType), ReportType.Reimbursement));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in thread: " + e.Message);
                throw;
            }
        }

        public void PrintMinisterService(int ministerId)
        {
            // Set report parameters
            var parameterList = new List<ReportParam>
                                    {
                                        new ReportParam
                                            {
                                                ParameterName = "ministerId",
                                                ParameterValue = ministerId.ToString()
                                            }
                                    };
            
            PrintMinisterService(parameterList);
        }

        private void PrintMinisterService(object reportParams)
        {
            try
            {
                var rsExecService = new ReportExecutionService
                {
                    Credentials = credentials,
                    Url = SettingsUtility.GetAppSetting(Resources.ReportServer)
                };

                // Set report path
                var reportPath = SettingsUtility.GetAppSetting(Resources.MinisterServiceReportPath);
                rsExecService.LoadReport(reportPath, null);

                var parameters = SetReportParameters((List<ReportParam>)reportParams);
                rsExecService.SetExecutionParameters(parameters, "en-us");

                // Init properties struct
                var printPropertiesStruct = new PrintPropertiesStruct
                {
                    rsExecutionService = rsExecService,
                    reportname = Enum.GetName(typeof(ReportType), ReportType.MinisterService)
                };

                // Add struct to print properities hashtable
                printProperties.Add(settingsCounter, printPropertiesStruct);

                // Render report to PDF and print
                //RenderAndPrintPDF(ReportType.MinisterService, Enum.GetName(typeof(ReportType), ReportType.MinisterService));
                
                //Print report
                PrintReport(ReportType.MinisterService, Enum.GetName(typeof(ReportType), ReportType.MinisterService));
            }
            catch (Exception e)
            {
                Console.WriteLine("Error in thread: " + e.Message);
                throw;
            }
        }

        public ParameterValue[] SetReportParameters(List<ReportParam> parameterObjList)
        {
            var parameters = new ParameterValue[parameterObjList.Count];

            for (var i = 0; i < parameterObjList.Count; i++)
            {
                parameters[i] = new ParameterValue
                                    {
                                        Label = parameterObjList[i].ParameterName,
                                        Name = parameterObjList[i].ParameterName,
                                        Value = parameterObjList[i].ParameterValue
                                    };
            }

            return parameters;
        }

        private bool PrintReport(ReportType reportType, string documentName)
        {
            this.RenderedReport = this.RenderReport();
            
            try
            {
                // Get printproperties from hashtable               
                var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
                int locNoOfPages = locStruct.noOfPages;
                
                // Wait for the report to completely render.
                if (locNoOfPages < 1)
                    return false;

                var pd = new PrintDocument();

                if (reportType == ReportType.MarriageRegistrationForm)
                {
                    //var copies = Convert.ToInt16(SettingsUtility.GetAppSetting(Resources.MarriageRegistrationFormCopies));
                    //var printerSettings = new PrinterSettings { Copies = copies };
                    //var printerSettings = new PrinterSettings { Copies = 1 };
                    var printerSettings = new PrinterSettings { PrinterName = "\\\\HP Color LaserJet 5500 PCL 6\\VigselBevis" };
                    pd.PrinterSettings = printerSettings;
                }
                else if (reportType == ReportType.MarriageRegister)
                {
                    //var copies = Convert.ToInt16(SettingsUtility.GetAppSetting(Resources.MarriageRegisterCopies));
                    //var printerSettings = new PrinterSettings { Copies = copies };
                    //var printerSettings = new PrinterSettings { Copies = 1 };
                    var printerSettings = new PrinterSettings { PrinterName = "\\\\HP Color LaserJet 5500 PCL 6\\VigselBevis" };
                    pd.PrinterSettings = printerSettings;
                    pd.DefaultPageSettings.Landscape = true;                  
                }
                else if (reportType == ReportType.MarriageCertificate)
                {                                     
                    var printerSettings = new PrinterSettings {PrinterName = "\\\\printsrv01\\VigselBevis"};
                    pd.PrinterSettings = printerSettings;
                    pd.DefaultPageSettings.Landscape = true;                    
                }

                // Update struct in hashtable
                locStruct.curPrintingPage = 1;
                locStruct.lstPrintingPage = locNoOfPages;
                printProperties.Remove(settingsCounter);
                printProperties.Add(settingsCounter, locStruct);
                
                if (pd.PrinterSettings.IsValid)
                {
                    // Print report
                    Console.WriteLine("Printing report...");
                    //pd.DocumentName = documentName;
                    //pd.PrintPage += pd_PrintPage;
                    pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
                    pd.Print();

                    printProperties.Remove(settingsCounter);
                }
                else
                {
                    Console.WriteLine("Printer is invalid.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return true;
        }

        public void PrintPDF()
        {
            var pd = new PrintDocument();

            var P = new Process
                        {
                            StartInfo =
                            {
                                    FileName = "C:/DeployFolder/Reports/ReportsPrintPDF/test.pdf",
                                    WindowStyle = ProcessWindowStyle.Hidden,
                                    Verb = "PrintTo",
                                    Arguments = pd.PrinterSettings.PrinterName.ToString(),
                                    CreateNoWindow = true
                                }
                        };


            //!! Since the file name involves a nonexecutable file(.pdf file), including a verb to specify what action to take on the file. //The action in our case is to "Print" a file in a selected printer.

            //!! Print the document in the printer selected in the PrintDialog !!//

            //!! Start the process !!//      

            P.Start();

            //!! P.WaitForExit(4000);     

            P.CloseMainWindow();
        }

        public void RenderAndPrintPDF(ReportType reportType, string report)
        {
            // Get printproperties from hashtable            
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            ReportExecutionService locRSExecService = locStruct.rsExecutionService;

            string ReportPath = SettingsUtility.GetAppSetting(Resources.PDFFolder) + report + DateTime.Now.ToString("yyyy-MM-dd HHmmss") + ".pdf";

            string extension;
            string encoding;
            string mimeType;
            string gsArguments;
            Warning[] warnings;
            string[] streamIDs;
            string strFormat = "PDF";
            string strDeviceInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";

            Byte[] result = null;
            result = locRSExecService.Render(strFormat, strDeviceInfo, out extension, out mimeType, out encoding, out warnings, out streamIDs);

            using (FileStream fs = new FileStream(ReportPath, FileMode.Create))
            {
                fs.Write(result, 0, result.Length);
                fs.Flush();
            }
            try
            {
                if (reportType == ReportType.MarriageRegister)
                {
                    var pd = new PrintDocument();

                    string printerName = pd.PrinterSettings.PrinterName;
                    gsArguments = string.Format("-grey -noquery -landscape -printer \"" + printerName + "\"" + " \"" + ReportPath + "\"");
                }
                else if (reportType == ReportType.MarriageCertificate)
                {
                    string printerName = SettingsUtility.GetAppSetting(Resources.CertificatePrinter);
                    gsArguments = string.Format("-grey -noquery -landscape -printer \"" + printerName + "\"" + " \"" + ReportPath + "\"");
                }
                else
                {
                    var pd = new PrintDocument();

                    string printerName = pd.PrinterSettings.PrinterName;
                    gsArguments = string.Format("-grey -noquery -printer \"" + printerName + "\"" + " \"" + ReportPath + "\"");
                }

                var GSPrintPath = SettingsUtility.GetAppSetting(Resources.GSPrint);

                string gsLocation = @GSPrintPath; //@"C:\Program Files\Ghostgum\gsview\gsprint.exe";
                var gsProcessInfo = new ProcessStartInfo
                                        {
                                            WindowStyle = ProcessWindowStyle.Hidden,
                                            FileName = gsLocation,
                                            Arguments = gsArguments
                                        };
                Process gsProcess = Process.Start(gsProcessInfo);
                gsProcess.WaitForExit();
                
            }
            finally
            {
                File.Delete(ReportPath);
            }
        }

        public byte[][] RenderReport()
        {
            // Private variables for rendering
            const string format = "IMAGE";
            string[] streamIDs;
            Byte[][] pages = null;

            // Get printproperties from hashtable            
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            ReportExecutionService locRSExecService = locStruct.rsExecutionService;

            // Build device info based on the start page
            string deviceInfo = String.Format(@"<DeviceInfo><OutputFormat>{0}</OutputFormat></DeviceInfo>", "emf");

            //Exectute the report and get page count.
            try
            {
                string extension;
                string encoding;
                string mimeType;
                Warning[] warnings;

                // Renders the first page of the report and returns streamIDs for 
                // subsequent pages
                byte[] firstPage = locRSExecService.Render(
                    format,
                    deviceInfo,
                    out extension,
                    out mimeType,
                    out encoding,
                    out warnings,
                    out streamIDs
                    );

                // The total number of pages of the report is 1 + the streamIDs         
                int locNoOfPages = streamIDs.Length + 1;
                pages = new Byte[locNoOfPages][];

                // The first page was already rendered
                pages[0] = firstPage;

                for (var pageIndex = 1; pageIndex < locNoOfPages; pageIndex++)
                {
                    // Build device info based on start page
                    deviceInfo =
                       String.Format(@"<DeviceInfo><OutputFormat>{0}</OutputFormat><StartPage>{1}</StartPage></DeviceInfo>",
                         "emf", pageIndex + 1);
                    pages[pageIndex] = locRSExecService.Render(
                       format,
                       deviceInfo,
                       out extension, 
                       out mimeType,
                       out encoding,
                       out warnings,
                       out streamIDs);
                }

                // Update properties hashtable
                locStruct.rsExecutionService = locRSExecService;
                locStruct.noOfPages = locNoOfPages;
                printProperties.Remove(settingsCounter);
                printProperties.Add(settingsCounter, locStruct);
            }
            catch (SoapException ex)
            {
                Console.WriteLine(ex.Detail.InnerXml);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if(pages != null)
                    Console.WriteLine("Number of pages: {0}", pages.Length);
                else
                    Console.WriteLine("Zero number of pages");
            }
            return pages;
        }

        private void pd_PrintPage(object sender, PrintPageEventArgs ev)
        {

            // Get printproperties from hashtable
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            int locCurrentPrintingPage = locStruct.curPrintingPage;
            int locLastPrintingPage = locStruct.lstPrintingPage;

            ev.HasMorePages = false;
            if (locCurrentPrintingPage <= locLastPrintingPage && MoveToPage(locCurrentPrintingPage))
            {
                // Draw the page
                ReportDrawPage(ev.Graphics);
                // If the next page is less than or equal to the last page, 
                // print another page.
                if (++locCurrentPrintingPage <= locLastPrintingPage)
                    ev.HasMorePages = true;
            }

            // Update print properties hashtable
            locStruct.curPrintingPage = locCurrentPrintingPage;
            locStruct.lstPrintingPage = locLastPrintingPage;
            printProperties.Remove(settingsCounter);
            printProperties.Add(settingsCounter, locStruct);
        }

        // Method to draw the current emf memory stream 
        private void ReportDrawPage(Graphics g)
        {
            // Get printproperties from hashtable            
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            Metafile locMetaFile = locStruct.metafile;
            MemoryStream locCurrentPageStream = locStruct.currentPageStream;

            if (null == locCurrentPageStream || 0 == locCurrentPageStream.Length || null == locMetaFile)
                return;
            lock (this)
            {
                // Set the metafile delegate.
                int width = locMetaFile.Width;
                int height = locMetaFile.Height;
                Graphics.EnumerateMetafileProc m_delegate = MetafileCallback;
                // Draw in the rectangle
                var destPoint = new Point(0, 0);
                g.EnumerateMetafile(locMetaFile, destPoint, m_delegate);
            }
            // Update print properties hashtable
            locStruct.metafile = locMetaFile;
            locStruct.currentPageStream = locCurrentPageStream;
            printProperties.Remove(settingsCounter);
            printProperties.Add(settingsCounter, locStruct);
        }

        private bool MoveToPage(Int32 page)
        {
            // Get printproperties from hashtable            
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            int locCurrentPrintingPage = locStruct.curPrintingPage;
            Metafile locMetaFile = locStruct.metafile;

            // Check to make sure that the current page exists in
            // the array list
            if (null == RenderedReport[locCurrentPrintingPage - 1])
                return false;
            // Set current page stream equal to the rendered page
            var locCurrentPageStream = new MemoryStream(this.RenderedReport[locCurrentPrintingPage - 1]) {Position = 0};
            // Set its postion to start.
            // Initialize the metafile
            if (null != locMetaFile)
            {
                locMetaFile.Dispose();
                locMetaFile = null;
            }
            // Load the metafile image for this page
            locMetaFile = new Metafile((Stream)locCurrentPageStream);

            // Update print properties hashtable
            locStruct.curPrintingPage = locCurrentPrintingPage;
            locStruct.currentPageStream = locCurrentPageStream;
            locStruct.metafile = locMetaFile;
            printProperties.Remove(settingsCounter);
            printProperties.Add(settingsCounter, locStruct);

            return true;
        }

        private bool MetafileCallback(EmfPlusRecordType recordType, int flags, int dataSize, IntPtr data, 
            PlayRecordCallback callbackData)
        {
            // Get printproperties from hashtable
            var locStruct = (PrintPropertiesStruct)printProperties[settingsCounter];
            Metafile locMetaFile = locStruct.metafile;

            byte[] dataArray = null;
            // Dance around unmanaged code.
            if (data != IntPtr.Zero)
            {
                // Copy the unmanaged record to a managed byte buffer 
                // that can be used by PlayRecord.
                dataArray = new byte[dataSize];
                Marshal.Copy(data, dataArray, 0, dataSize);
            }
            // play the record.      
            locMetaFile.PlayRecord(recordType, flags, dataSize, dataArray);

            // Update print properties hashtable
            locStruct.metafile = locMetaFile;
            printProperties.Remove(settingsCounter);
            printProperties.Add(settingsCounter, locStruct);

            return true;
        }

        public byte[][] RenderedReport { get; set; }  
    }
}
